读完了胡子大哈的React.js 小书后,总结一下对 redux 的新理解吧,主要是 store,connect,reducer,provider 等内容的一些浅见。
redux 的出现,事实上为了解决状态管理的问题。最直接的问题就是,如何在各个组件之间共享状态(即组件间通讯)。
Provider
redux 应对上述问题的解决方案,就是将这些需要共享的状态放置在根组件的context中(类似于全局变量),其子组件都能获取到 context。
具体的做法,其实是将 Index 组件(根组件)包装成 Provider 组件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| export class Provider extends Component { static propTypes = { store: PropTypes.object, children: PropTypes.any, };
static childContextTypes = { store: PropTypes.object, };
getChildContext() { return { store: this.props.store, }; }
render() { return <div>{this.props.children}</div>; } }
|
这个 Provider 组件,为整个组件树添加了 store,任何子组件都能获取到 store
1 2 3
| <Provider store={store}> <Index /> </Provider>
|
Store
store 算是组件的状态管理器,也是整个 redux 的核心。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| function createStore(reducer) { let state = null; const listeners = []; const subscribe = (listener) => listeners.push(listener); const getState = () => state; const dispatch = (action) => { state = reducer(state, action); listeners.forEach((listener) => listener()); }; dispatch({}); return { getState, dispatch, subscribe, }; }
|
store 做的事情包括以下三件:
- getState:用于获取当前的状态
- dispatch:修改状态(reducer),并将事件分发给所有监听器
- subscribe:订阅事件,用它来增加新的监听器
Reducer
reducer 由用户自己定义,有两个参数,前者是老状态,后者是一个 action。
它做的事情仅仅是:初始化和计算新的 state;根据 action 的信息,reducer 修改对应的状态,并返回一个新状态(全新的对象,而不是在老对象上修改)。
举个书中的例子:
1 2 3 4 5 6 7 8 9 10 11 12
| const themeReducer = (state, action) => { if (!state) return { themeColor: "red", }; switch (action.type) { case "CHANGE_COLOR": return { ...state, themeColor: action.themeColor }; default: return state; } };
|
reducer,按照规定,应该是一个纯函数。
Connect
connect 从本质上讲,是一个高阶组件,它的作用可以理解为:
- 子组件通过 connect 成为高阶组件后,其内部不用再去对 context 进行读写(降低依赖性)
- 经过 connect 后,从 context 的 store 里面读取到的 state/dispatch/props,将会统一以 props 的状态传入子组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| export const connect = (mapStateToProps, mapDispatchToProps) => ( WrappedComponent ) => { class Connect extends Component { static contextTypes = { store: PropTypes.object, };
constructor() { super(); this.state = { allProps: {}, }; }
_updateProps() { const { store } = this.context; let stateProps = mapStateToProps ? mapStateToProps(store.getState(), this.props) : {}; let dispatchProps = mapDispatchToProps ? mapDispatchToProps(store.dispatch, this.props) : {}; this.setState({ allProps: { ...stateProps, ...dispatchProps, ...this.props, }, }); }
componentWillMount() { const { store } = this.context; this._updateProps(); store.subscribe(() => this._updateProps()); }
render() { return <WrappedComponent {...this.state.allProps} />; } }
return Connect; };
|